#include "ngfinterf.h"
#include <iostream>

#define STORE_TYPE unsigned short int
#define STORE_TYPE_MAX ((2<<sizeof(STORE_TYPE))-1)

using namespace std;

NeuralGraphicsFileInterface::NeuralGraphicsFileInterface(Message* msg, Parameters* params) {
	
	// Intialize variables
	weights=NULL;
	outputs=NULL;
	weights_size=0;
	outputs_size=0;
	
	has_errors=0;
	
	message=msg;
	parameters=params;
	
	real_height=1;
	real_width=1;
	
	sample_count_h=1;
	sample_count_w=1;
	
	hidden_count=1;
	output_width=1;
	output_height=1;
	
	color=0;
}

NeuralGraphicsFileInterface::~NeuralGraphicsFileInterface() {
	freeWeightsOutputs();
}

void NeuralGraphicsFileInterface::allocateWeightsOutputs() {
	
	// 1. make sure the Weights/Outputs are free
	freeWeightsOutputs();
	
	// 2. set the values according to the data input
	weights_size=output_height*output_width;
	weights_internal_size=hidden_count;
	outputs_internal_size=hidden_count;	
	if (parameters->getColor())
		outputs_size=sample_count_h*sample_count_w*3;
	else
		outputs_size=sample_count_h*sample_count_w;
	
	// 3. allocate
	weights = new double*[weights_size];
	if (weights==NULL) { has_errors=1; return; }
	for (int i=0; i<weights_size; i++) {
		weights[i] = new double [weights_internal_size];
		if (weights[i]==NULL) { has_errors=1; return; }
	}
	
	outputs = new double*[outputs_size];
	if (outputs==NULL) { has_errors=1; return; }
	for (int i=0; i<outputs_size; i++) {
		outputs[i] = new double [outputs_internal_size];
		if (outputs[i]==NULL) { has_errors=1; return; }
	}
	
}


int NeuralGraphicsFileInterface::areWeightsOutputsAllocated() {
	if ((weights!=NULL)&&(outputs!=NULL))
		return 1;
	else 
		return 0;
}


void NeuralGraphicsFileInterface::freeWeightsOutputs() {
		
	// 1. delete the weights, if that's the case
	if (weights!=NULL) {
		for (int i=0; i<weights_size; i++) {
			delete[] weights[i];
		}
		delete[] weights;
		weights=NULL;
	}
		
	// 2, delete the outputs, if that's the case
	if (outputs!=NULL) {
		for (int i=0; i<outputs_size; i++) {
			delete[] outputs[i];
		}
		delete[] outputs;
		outputs=NULL;
	}
	
}

int NeuralGraphicsFileInterface::getRealHeight() {
	return real_height;
}

int NeuralGraphicsFileInterface::getRealWidth() {
	return real_width;
}

int NeuralGraphicsFileInterface::getColor() {
	return color;
}

int NeuralGraphicsFileInterface::getHiddenNeuronCount() {
	return hidden_count;
}

int NeuralGraphicsFileInterface::getOutputSampleHeight() {
	return sample_count_h;
}

int NeuralGraphicsFileInterface::getOutputSampleWidth() {
	return sample_count_w;
}

int NeuralGraphicsFileInterface::getOutputNeuronMatrixHeight() {
	return output_height;
}

int NeuralGraphicsFileInterface::getOutputNeuronMatrixWidth() {
	return output_width;
}

double* NeuralGraphicsFileInterface::getOutputNeuronWeight(int neuron) {
	if (neuron<weights_size)
		return weights[neuron];
	else
		return NULL;
}

double* NeuralGraphicsFileInterface::getPreOutputSample(int sampleno) {
	if (sampleno<outputs_size)
		return outputs[sampleno];
	else
		return NULL;	
}

void NeuralGraphicsFileInterface::setRealSize(int height, int width) {
	real_height = height;
	real_width = width;
}

void NeuralGraphicsFileInterface::setHiddenNeuronCount(int hidden) {
	hidden_count = hidden;
}

void NeuralGraphicsFileInterface::setOutputMatrixSize(int height, int width){
	output_height = height;
	output_width  = width;
}

void NeuralGraphicsFileInterface::setSampleMatrixSize(int sample_h, int sample_w){
	sample_count_h = sample_h;
	sample_count_w = sample_w;
}

void NeuralGraphicsFileInterface::setColor(int isColor) {
	color = isColor; 
}

void NeuralGraphicsFileInterface::setOutputNeuronWeight(int neuron, double* crtweights) {
	if ((neuron<weights_size)&&(weights!=NULL)&&(crtweights!=NULL))
		memcpy(weights[neuron], crtweights, weights_internal_size*sizeof(double) );
	else
		message->printError("Internal Error NFGEngine.setOutputNeuronWeight.\n");
}

void NeuralGraphicsFileInterface::setPreOutputSample(int sampleno, double* sample) {
	if ((sampleno<outputs_size)&&(outputs!=NULL)&&(sample!=NULL))
		memcpy(outputs[sampleno], sample, outputs_internal_size*sizeof(double) );
	else
		message->printError("Internal Error NFGEngine.setPreOutputSample.\n");	
}

void NeuralGraphicsFileInterface::readNGFFile() {
	has_errors = readNGFFileInternal();
}

int NeuralGraphicsFileInterface::readNGFFileInternal() {

	// 1. initialization
	int error=0;
	int i, j;
	char crt;
	//STORE_TYPE crtcolor;
	
	has_errors=0;
	
	// 2. open file
	ifstream in(parameters->getInputFilename(), ios::in|ios::binary);

	if(!in)
		return 1;

	cerr.flush();

	// 3. header
	in.get(crt); if((crt!='N')||!in) error=1;
	in.get(crt); if((crt!='G')||!in) error=1;
	in.get(crt); if((crt!='F')||!in) error=1;
	in.get(crt); if((crt!='1')||!in) error=1;

	if (error) {
		in.close();
		return 1;
	}
	
	in.read((char*)&real_width,sizeof(int));
	in.read((char*)&real_height,sizeof(int));
	in.read((char*)&sample_count_w,sizeof(int));
	in.read((char*)&sample_count_h,sizeof(int));
	in.read((char*)&hidden_count,sizeof(int));
	in.read((char*)&output_width,sizeof(int));
	in.read((char*)&output_height,sizeof(int));
	in.read((char*)&color,sizeof(int));

	if (parameters->getVerbose()) {
		message->printMessage("NGF Information:\n real_width=");
		message->printMessage(real_width);
		message->printMessage("\n real_height=");
		message->printMessage(real_height);
		message->printMessage("\n sample_count_w=");
		message->printMessage(sample_count_w);
		message->printMessage("\n sample_count_h=");
		message->printMessage(sample_count_h);
		message->printMessage("\n hidden_count=");
		message->printMessage(hidden_count);
		message->printMessage("\n output_width=");
		message->printMessage(output_width);
		message->printMessage("\n output_height=");
		message->printMessage(output_height);
		message->printMessage("\n color=");
		message->printMessage(color);
		message->printMessage("\n");		
	}
	
	parameters->setColor(color);

	in.get(crt); if((crt!='N')||!in) error=1;

	if (error) {
		in.close();
		return 1;
	}
	
	// 4. allocate weights and outputs according to the header
	allocateWeightsOutputs();
	
	for (i=0; i<weights_size; i++)
		for (j=0; j<weights_internal_size; j++) 
			in.read((char*)&(weights[i][j]), sizeof(double));
	
	in.get(crt); if((crt!='G')||!in) error=1;

	if (error) {
		in.close();
		return 1;
	}

	for (i=0; i<outputs_size; i++)
		for (j=0; j<outputs_internal_size; j++) 
		{	
			in.read((char*)&(outputs[i][j]), sizeof(double));
			//in.read((char*)&crtcolor, sizeof(STORE_TYPE));
			//outputs[i][j]=((double)crtcolor/(double)(STORE_TYPE_MAX));
		}
	
	in.get(crt); if((crt!='F')||!in) error=1;

	if (error) {
		in.close();
		return 1;
	}

	in.close();
	return 0;
}

void NeuralGraphicsFileInterface::writeNGFFile() {
	has_errors = writeNGFFileInternal();
}

int NeuralGraphicsFileInterface::writeNGFFileInternal() {

	// 1. initialization
	int color, i, j;
	// STORE_TYPE storer;
	
	has_errors=0;

	if (!areWeightsOutputsAllocated()) {
		message->printError("Internal Error: NFG_Engine.writeNGFFileInternal ");
		message->printError("called when weights and outputs were not ");
		message->printError("allocated!\n");
		return 1;
	}

	
	// 2. open file
	ofstream out(parameters->getOutputFilename(), ios::out|ios::binary);

	if(!out)
		return 1;

	// 3. header
	out.put('N');
	out.put('G');
	out.put('F');
	out.put('1');
	
	color=parameters->getColor();
	
	out.write((const char*)&real_width,sizeof(int));
	out.write((const char*)&real_height,sizeof(int));
	out.write((const char*)&sample_count_w,sizeof(int));
	out.write((const char*)&sample_count_h,sizeof(int));
	out.write((const char*)&hidden_count,sizeof(int));
	out.write((const char*)&output_width,sizeof(int));
	out.write((const char*)&output_height,sizeof(int));
	out.write((const char*)&color,sizeof(int));
		
	out.put('N');

	// 4. allocate weights and outputs according to the header
	
	for (i=0; i<weights_size; i++) 
		for (j=0; j<weights_internal_size; j++) 
			out.write((char*)&(weights[i][j]), sizeof(double));
	
	out.put('G');
	
	for (i=0; i<outputs_size; i++)
		for (j=0; j<outputs_internal_size; j++) 
		{
			//storer=(STORE_TYPE)(outputs[i][j]*STORE_TYPE_MAX);
			//out.write((char*)&storer, sizeof(STORE_TYPE));
			out.write((char*)&(outputs[i][j]), sizeof(double));
		}
	
	out.put('F');

	out.close();
	return 0;
}

int NeuralGraphicsFileInterface::hasErrors() {
	return has_errors;
}

